#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>

#include "cdefs.h"
#include "cbin.h"
#include "cdes.h"

#define FAIL(str, line) {                       \
  printf("Fail on line %d: [%s]\n", line, str); \
  return str;                                   \
}

#define ASSERT(expr) {                  \
  static_num_tests++;                   \
  if (!(expr)) FAIL(#expr, __LINE__);   \
}

#define RUN_TEST(test) {        \
    const char *msg = test();   \
    if(msg)                     \
        return msg;             \
}


static int static_num_tests = 0;

char *data[] = {
    //key              plaintext          ciphertext
    "0000000000000000","0000000000000000","8CA64DE9C1B123A7",
    "0000000000000000","FFFFFFFFFFFFFFFF","355550B2150E2451",
    "0101010101010101","0123456789ABCDEF","617B3A0CE8F07100",
    "0113B970FD34F2CE","059B5E0851CF143A","86A560F10EC6D85B",
    "0123456789ABCDEF","0000000000000000","D5D44FF720683D0D",
    "0123456789ABCDEF","1111111111111111","17668DFC7292532D",
    "0131D9619DC1376E","5CD54CA83DEF57DA","7A389D10354BD271",
    "0170F175468FB5E6","0756D8E0774761D2","0CD3DA020021DC09",
    "018310DC409B26D6","1D9D5C5018F728C2","5F4C038ED12B2E41",
    "025816164629B007","480D39006EE762F2","A1F9915541020B56",
    "04689104C2FD3B2F","26955F6835AF609A","5C513C9C4886C088",
    "04B915BA43FEB5B6","42FD443059577FA2","AF37FB421F8C4095",
    "07A1133E4A0B2686","0248D43806F67172","868EBB51CAB4599A",
    "07A7137045DA2A16","3BDD119049372802","DFD64A815CAF1A0F",
    "1111111111111111","0123456789ABCDEF","8A5AE1F81AB8F2DD",
    "1111111111111111","1111111111111111","F40379AB9E0EC533",
    "1C587F1C13924FEF","305532286D6F295A","63FAC0D034D9F793",
    "1F08260D1AC2465E","6B056E18759F5CCA","EF1BF03E5DFA575A",
    "1F1F1F1F0E0E0E0E","0123456789ABCDEF","DB958605F8C8C606",
    "3000000000000000","1000000000000001","958E6E627A05557B",
    "37D06BB516CB7546","164D5E404F275232","0A2AEEAE3FF4AB77",
    "3849674C2602319E","51454B582DDF440A","7178876E01F19B2A",
    "43297FAD38E373FE","762514B829BF486A","EA676B2CB7DB2B7A",
    "49793EBC79B3258F","437540C8698F3CFA","6FBF1CAFCFFD0556",
    "49E95D6D4CA229BF","02FE55778117F12A","5A6B612CC26CCE4A",
    "4FB05E1515AB73A7","072D43A077075292","2F22E49BAB7CA1AC",
    "584023641ABA6176","004BD6EF09176062","88BF0DB6D70DEE56",
    "7CA110454A1A6E57","01A1D6D039776742","690F5B0D9A26939B",
    "E0FEE0FEF1FEF1FE","0123456789ABCDEF","EDBFD1C66C29CCC7",
    "FEDCBA9876543210","0123456789ABCDEF","ED39D950FA74BCC4",
    "FEDCBA9876543210","FFFFFFFFFFFFFFFF","2A2BB008DF97C2F2",
    "FFFFFFFFFFFFFFFF","0000000000000000","CAAAAF4DEAF1DBAE",
    "FFFFFFFFFFFFFFFF","FFFFFFFFFFFFFFFF","7359B2163E4EDC58"
};

const char *test_cdes_ecb_encrypt() {
    unsigned char plaintext[8];
    unsigned char ciphertext[8];
    unsigned char key[8];
	unsigned char tmp[8];
	int i;

	for(i = 0; i < ARRAY_SIZE(data); i += 3) {
		cbin_hex_to_bin((unsigned char *)data[i], key, 16);
		cbin_hex_to_bin((unsigned char *)data[i+1], plaintext, 16);
		cbin_hex_to_bin((unsigned char *)data[i+2], ciphertext, 16);
		
		cdes_ecb_encrypt(plaintext, tmp, key, 8);	
		ASSERT(memcmp(tmp, ciphertext, 8) == 0);
	}

    return NULL;
}

const char *test_cdes_ecb_decrypt() {
    unsigned char plaintext[8];
    unsigned char ciphertext[8];
    unsigned char key[8];
	unsigned char tmp[8];
	int i;

	for(i = 0; i < ARRAY_SIZE(data); i += 3) {
		cbin_hex_to_bin((unsigned char *)data[i], key, 16);
		cbin_hex_to_bin((unsigned char *)data[i+1], plaintext, 16);
		cbin_hex_to_bin((unsigned char *)data[i+2], ciphertext, 16);
		
		cdes_ecb_decrypt(ciphertext, tmp, key, 8);	
		ASSERT(memcmp(tmp, plaintext, 8) == 0);
	}

    return NULL;
}


static const char *run_all_tests(void) {
    RUN_TEST(test_cdes_ecb_encrypt);
	RUN_TEST(test_cdes_ecb_decrypt);
    return NULL;
}

int main(void) {
    const char *fail_msg = run_all_tests();
    printf("%s, tests run: %d\n", fail_msg ? "FAIL" : "PASS", static_num_tests);
    return fail_msg == NULL ? 0 : -1;
}

